1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.hiprenderer.renderer; 12 public import hip.config.renderer; 13 public import hip.hiprenderer.shader; 14 public import hip.hiprenderer.vertex; 15 public import hip.hiprenderer.framebuffer; 16 public import hip.hiprenderer.viewport; 17 public import hip.api.renderer.texture; 18 public import hip.api.renderer.operations; 19 public import hip.api.graphics.color; 20 public import hip.api.renderer.core; 21 public import hip.api.renderer.shadervar; 22 import hip.windowing.window; 23 import hip.math.rect; 24 import hip.error.handler; 25 import hip.console.log; 26 27 28 private struct HipRendererResources 29 { 30 IHipTexture[] textures; 31 Shader[] shaders; 32 IHipVertexArrayImpl[] vertexArrays; 33 IHipRendererBuffer[] buffers; 34 } 35 36 class HipRendererImplementation : IHipRenderer 37 { 38 static struct Statistics 39 { 40 ulong drawCalls; 41 ulong renderFrames; 42 } 43 protected Viewport currentViewport; 44 protected Viewport mainViewport; 45 protected IHipRendererImpl rendererImpl; 46 protected HipRendererMode rendererMode; 47 protected Statistics stats; 48 public HipWindow window = null; 49 public Shader currentShader; 50 package HipRendererType rendererType = HipRendererType.None; 51 52 public uint width, height; 53 protected HipRendererConfig currentConfig; 54 55 protected HipRendererResources res; 56 protected bool depthTestingEnabled; 57 protected HipDepthTestingFunction currentDepthTestFunction; 58 59 protected IHipRendererBuffer quadIndexBuffer; 60 61 public bool initialize (string confData, string confPath) 62 { 63 import hip.config.opts; 64 import hip.data.ini; 65 import hip.hiprenderer.initializer; 66 HipINI ini = HipINI.parse(confData, confPath); 67 HipRendererConfig cfg; 68 rendererType = getRendererTypeFromVersion(); 69 int renderWidth = HIP_DEFAULT_WINDOW_SIZE[0]; 70 int renderHeight = HIP_DEFAULT_WINDOW_SIZE[1]; 71 string defaultRenderer = "OpenGL3"; 72 version(AppleOS) defaultRenderer = "Metal"; 73 if(!ini.configFound || !ini.noError) 74 { 75 import hip.util.string; 76 if(!ini.configFound) 77 logln("No renderer.conf found"); 78 if(!ini.noError) 79 { 80 logln("Renderer.conf parsing error"); 81 rawerror(BigString(ini.errors).toString); 82 } 83 hiplog("Defaulting renderer to "~defaultRenderer); 84 } 85 else 86 { 87 cfg.bufferingCount = ini.tryGet!ubyte("buffering.count", 2); 88 cfg.multisamplingLevel = ini.tryGet!ubyte("multisampling.level", 0); 89 cfg.fullscreen = ini.tryGet("screen.fullscreen", false); 90 cfg.vsync = ini.tryGet("vsync.on", true); 91 92 renderWidth = ini.tryGet("screen.width", renderWidth); 93 renderHeight = ini.tryGet("screen.height", renderHeight); 94 string renderer = ini.tryGet("screen.renderer", "GL3"); 95 rendererType = rendererFromString(renderer); 96 } 97 return initialize(getRendererWithFallback(rendererType), &cfg, renderWidth, renderHeight); 98 } 99 100 public Statistics getStatistics(){return stats;} 101 version(dll) public bool initExternal(HipRendererType type, int windowWidth = -1, int windowHeight = -1) 102 { 103 import hip.hiprenderer.initializer; 104 rendererType = type; 105 if(windowWidth == -1) 106 windowWidth = 1920; 107 if(windowHeight == -1) 108 windowHeight = 1080; 109 return initialize(getRendererWithFallback(type), null, cast(uint)windowWidth, cast(uint)windowHeight, true); 110 } 111 112 private HipWindow createWindow(uint width, uint height) 113 { 114 HipWindow wnd = new HipWindow(width, height, HipWindowFlags.DEFAULT); 115 version(Android){} 116 else wnd.start(); 117 return wnd; 118 } 119 120 /** 121 * Populates a buffer with indices forming quads 122 * If the quadsCount is bigger than the existing one, throws since 123 * it probably can be set at compile time and it is easier to control like that 124 */ 125 public IHipRendererBuffer getQuadIndexBuffer(size_t quadsCount) 126 { 127 if(!quadIndexBuffer) 128 { 129 import hip.util.array; 130 quadIndexBuffer = createBuffer(quadsCount*index_t.sizeof*6, HipResourceUsage.Immutable, HipRendererBufferType.index); 131 index_t[] output = uninitializedArray!(index_t[])(quadsCount*6); 132 index_t index = 0; 133 for(index_t i = 0; i < quadsCount; i++) 134 { 135 output[index+0] = cast(index_t)(i*4+0); 136 output[index+1] = cast(index_t)(i*4+1); 137 output[index+2] = cast(index_t)(i*4+2); 138 139 output[index+3] = cast(index_t)(i*4+2); 140 output[index+4] = cast(index_t)(i*4+3); 141 output[index+5] = cast(index_t)(i*4+0); 142 index+=6; 143 } 144 quadIndexBuffer.setData(output); 145 import core.memory; 146 GC.free(output.ptr); 147 } 148 149 return quadIndexBuffer; 150 } 151 152 public bool initialize (IHipRendererImpl impl, HipRendererConfig* config, uint width, uint height, bool isExternal = false) 153 { 154 ErrorHandler.startListeningForErrors("Renderer initialization"); 155 if(config != null) 156 currentConfig = *config; 157 currentConfig.logConfiguration(); 158 rendererImpl = impl; 159 window = createWindow(width, height); 160 ErrorHandler.assertErrorMessage(window !is null, "Error creating window", "Could not create Window"); 161 if(isExternal) 162 { 163 version(dll) 164 { 165 if(!rendererImpl.initExternal()) 166 { 167 ErrorHandler.showErrorMessage("Error Initializing Renderer", "Renderer could not initialize externally"); 168 return false; 169 } 170 } 171 } 172 else 173 rendererImpl.init(window); 174 window.setVSyncActive(currentConfig.vsync); 175 window.setFullscreen(currentConfig.fullscreen); 176 window.show(); 177 foreach(err; window.errors) 178 loglnError(err); 179 180 setWindowSize(width, height); 181 182 //After init 183 import hip.config.opts; 184 mainViewport = new Viewport(0,0, window.width, window.height); 185 setViewport(mainViewport); 186 setColor(); 187 HipRenderer.setRendererMode(HipRendererMode.triangles); 188 189 return ErrorHandler.stopListeningForErrors(); 190 } 191 public void setWindowSize(int width, int height) @nogc 192 { 193 assert(width > 0 && height > 0, "Window width and height must be greater than 0"); 194 logln("Changing window size to [", width, ", ", height, "]"); 195 window.setSize(cast(uint)width, cast(uint)height); 196 this.width = width; 197 this.height = height; 198 } 199 public HipRendererType getType(){return rendererType;} 200 201 /** 202 * Info is data that can't be changed from the renderer. 203 */ 204 public HipRendererInfo getInfo() 205 { 206 return HipRendererInfo( 207 getType, 208 rendererImpl.getShaderVarMapper 209 ); 210 } 211 212 public HipRendererConfig getCurrentConfig(){return currentConfig;} 213 public int getMaxSupportedShaderTextures(){return rendererImpl.queryMaxSupportedPixelShaderTextures();} 214 215 216 public IHipTexture getTextureImplementation(HipResourceUsage usage = HipResourceUsage.Immutable) 217 { 218 res.textures~= rendererImpl.createTexture(usage); 219 return res.textures[$-1]; 220 } 221 222 public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 223 { 224 rendererImpl.setColor(r,g,b,a); 225 } 226 227 public Viewport getCurrentViewport() @nogc {return currentViewport;} 228 public void setViewport(Viewport v) 229 { 230 this.currentViewport = v; 231 v.updateForWindowSize(width, height); 232 rendererImpl.setViewport(v); 233 } 234 235 public void reinitialize() 236 { 237 version(Android) 238 { 239 foreach(tex; res.textures) 240 { 241 // (cast(Hip_GL3_Texture)tex).reload(); 242 } 243 foreach(shader; res.shaders) 244 { 245 shader.reload(); 246 } 247 } 248 } 249 250 public void setCamera() 251 { 252 253 } 254 /** 255 * Fixes the matrix order based on the config and renderer. 256 * If the renderer is column and the config is row, it will tranpose 257 */ 258 public T getMatrix(T)(auto ref T mat) 259 { 260 if(currentConfig.isMatrixRowMajor && !rendererImpl.isRowMajor()) 261 return mat.transpose(); 262 return mat; 263 } 264 265 Shader newShader() 266 { 267 res.shaders~= new Shader(rendererImpl.createShader()); 268 return res.shaders[$-1]; 269 } 270 public Shader newShader(string vertexShaderPath, string fragmentShaderPath) 271 { 272 Shader ret = newShader(); 273 ret.loadShadersFromFiles(vertexShaderPath, fragmentShaderPath); 274 return ret; 275 } 276 277 public HipFrameBuffer newFrameBuffer(int width, int height, Shader frameBufferShader = null) 278 { 279 return new HipFrameBuffer(rendererImpl.createFrameBuffer(width, height), width, height, frameBufferShader); 280 } 281 public IHipVertexArrayImpl createVertexArray() 282 { 283 res.vertexArrays~= rendererImpl.createVertexArray(); 284 return res.vertexArrays[$-1]; 285 } 286 287 public IHipRendererBuffer createBuffer(size_t size, HipResourceUsage usage, HipRendererBufferType type) 288 { 289 res.buffers~= rendererImpl.createBuffer(size, usage, type); 290 return res.buffers[$-1]; 291 } 292 public void setShader(Shader s) 293 { 294 currentShader = s; 295 s.bind(); 296 } 297 public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line =__LINE__) 298 { 299 return rendererImpl.hasErrorOccurred(err, file, line); 300 } 301 302 public void exitOnError(string file = __FILE__, size_t line = __LINE__) 303 { 304 import hip.config.opts; 305 import core.stdc.stdlib:exit; 306 string err; 307 if(hasErrorOccurred(err, file, line)) 308 { 309 loglnError(err, file,":",line); 310 static if(CustomRuntime) 311 exit(-1); 312 else 313 throw new Error(err); 314 } 315 } 316 317 public void begin() 318 { 319 320 rendererImpl.begin(); 321 } 322 323 public void setErrorCheckingEnabled(bool enable = true) 324 { 325 rendererImpl.setErrorCheckingEnabled(enable); 326 } 327 328 public void setRendererMode(HipRendererMode mode) 329 { 330 if(mode != rendererMode) 331 { 332 rendererMode = mode; 333 rendererImpl.setRendererMode(mode); 334 } 335 } 336 public HipRendererMode getMode(){return rendererMode;} 337 338 public void drawIndexed(index_t count, uint offset = 0) 339 { 340 rendererImpl.drawIndexed(count, offset); 341 stats.drawCalls++; 342 } 343 public void drawIndexed(HipRendererMode mode, index_t count, uint offset = 0) 344 { 345 setRendererMode(mode); 346 HipRenderer.drawIndexed(count, offset); 347 stats.drawCalls++; 348 } 349 public void drawVertices(index_t count, uint offset = 0) 350 { 351 rendererImpl.drawVertices(count, offset); 352 } 353 public void drawVertices(HipRendererMode mode, index_t count, uint offset = 0) 354 { 355 rendererImpl.setRendererMode(mode); 356 HipRenderer.drawVertices(count, offset); 357 } 358 359 public void end() 360 { 361 rendererImpl.end(); 362 foreach(sh; res.shaders) sh.onRenderFrameEnd(); 363 stats.drawCalls=0; 364 stats.renderFrames++; 365 } 366 public void clear() 367 { 368 rendererImpl.clear(); 369 stats.drawCalls++; 370 } 371 public void clear(HipColorf color) 372 { 373 auto rgba = color.unpackRGBA; 374 rendererImpl.clear(rgba[0], rgba[1], rgba[2], rgba[3]); 375 stats.drawCalls++; 376 } 377 public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 378 { 379 rendererImpl.clear(r,g,b,a); 380 stats.drawCalls++; 381 } 382 HipDepthTestingFunction getDepthTestingFunction() const 383 { 384 return currentDepthTestFunction; 385 } 386 bool isDepthTestingEnabled() const 387 { 388 return depthTestingEnabled; 389 } 390 void setDepthTestingEnabled(bool enable) 391 { 392 rendererImpl.setDepthTestingEnabled(enable); 393 } 394 void setDepthTestingFunction(HipDepthTestingFunction fn) 395 { 396 rendererImpl.setDepthTestingFunction(fn); 397 currentDepthTestFunction = fn; 398 } 399 400 void setStencilTestingEnabled(bool bEnable) 401 { 402 rendererImpl.setStencilTestingEnabled(bEnable); 403 } 404 void setStencilTestingMask(uint mask) 405 { 406 rendererImpl.setStencilTestingMask(mask); 407 } 408 void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a) 409 { 410 rendererImpl.setColorMask(r,g,b,a); 411 } 412 void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask) 413 { 414 rendererImpl.setStencilTestingFunction(passFunc, reference, mask); 415 } 416 void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass) 417 { 418 rendererImpl.setStencilOperation(stencilFail, depthFail, stencilAndDephPass); 419 } 420 421 public void dispose() 422 { 423 rendererImpl.dispose(); 424 if(window !is null) 425 window.exit(); 426 window = null; 427 } 428 } 429 430 431 private __gshared HipRendererImplementation impl; 432 void PreInitializeHipRenderer() 433 { 434 impl = new HipRendererImplementation(); 435 setHipRenderer(impl); 436 } 437 438 439 pragma(inline, true) HipRendererImplementation HipRenderer(){return impl;} 440 441 export extern(System) IHipRenderer HipRendererAPI() 442 { 443 return HipRenderer; 444 } 445 446 void logConfiguration(HipRendererConfig config) 447 { 448 import hip.console.log; 449 with(config) 450 { 451 loglnInfo("Starting HipRenderer with configuration: ", 452 "\nMultisamplingLevel: ", multisamplingLevel, 453 "\nBufferingCount: ", bufferingCount, 454 "\nFullscreen: ", fullscreen, 455 "\nVsync: ", vsync? "activated" : "deactivated"); 456 } 457 }